/*-
 * Copyright 2012 Guram Dukashvili
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#ifndef __idx_h__
#define __idx_h__
//---------------------------------------------------------------------------
#include "config.h"
#include "cdc256.h"
#include "int.h"
#include "array.h"
#include "vector.h"
#include "list.h"
#include "hash.h"
#include "rwlock.h"
#include "thread.h"
//---------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
//---------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma pack(push)
#pragma pack(1)
#elif defined(__BORLANDC__)
#pragma option push -a1
#endif
//---------------------------------------------------------------------------
enum INDEX_BLOCK_TYPE {
  INDEX_LEAF = 0,
  INDEX_NODE = 1
};
//---------------------------------------------------------------------------
enum INDEX_LINK_CHANGE {
  ILC_NONE,
  ILC_NEW,
  ILC_DEL,
  ILC_UPD
};
//---------------------------------------------------------------------------
enum INDEX_FILE_TYPE {
  INDEX_IDX_FILE_HANDLE,
  INDEX_JRN_FILE_HANDLE
};
//---------------------------------------------------------------------------
struct PACKED signature {
  uint8_t data[32];
};
//---------------------------------------------------------------------------
struct PACKED index_file_header {
  struct cdc256 checksum;
  struct signature signature;
  uint32_t block_size;
};
//---------------------------------------------------------------------------
struct PACKED index_block {
  struct cdc256 checksum;
//  union {
//    byte_size_int prev_block_index;
//    uint8_t prev_block_index_align[BYTE_SIZE_INT_MAX];
//  };
  uint32_t  count;
  uint8_t   type; //enum INDEX_BLOCK_TYPE;
  uint8_t   keys[];
};
//---------------------------------------------------------------------------
struct PACKED index_journal_block_state { // variable size structure
  uint8_t what; // enum INDEX_LINK_CHANGE;
  byte_size_int block_index;
};
//---------------------------------------------------------------------------
struct PACKED index_journal_block { // variable size structure
  struct cdc256 checksum;
  struct signature signature;

  uint32_t block_count;
  struct index_journal_block_state blocks[1];
};
//---------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma pack(pop)
#elif defined(__BORLANDC__)
#pragma option pop
#endif
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
struct index_block_mapper_entry {
  union {
    byte_size_int block_index;
    uint8_t block_index_align[BYTE_SIZE_INT_MAX];
  };
  union {
    byte_size_int jrn_block_index;
    uint8_t jrn_block_index_align[BYTE_SIZE_INT_MAX];
  };
  int jrn_file_index;
};
//---------------------------------------------------------------------------
struct index;
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
struct index_link {
  union {
    byte_size_int block_index;
    uint8_t block_index_align[BYTE_SIZE_INT_MAX];
  };
  struct index * idx;
  struct hash_node hash_link;
  struct list_node list_link;
  struct list * list;
  struct rwlock lock;
  uint32_t linked;
  uint32_t serno;
#if COMPILE_RUNTIME_CHECKS
  bool io_inprogress;
#endif
  struct index_block blk;
};
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
struct index_link_change {
  struct index_link * link;
  enum INDEX_LINK_CHANGE what;
};
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
struct shared_cache {
  uintptr_t block_size;
  uint64_t extent_size;

  struct hash links_hash;
  struct list links_linked;
  struct list links_lru;
  struct mutex links_mutex;

  uintptr_t max_size;
  uintptr_t cur_size;
  uintmax_t hit_count;
  uintmax_t miss_count;
  uint64_t iowait_time;
  float high_watermark;
  float low_watermark;
  float flush_watermark;
};
//---------------------------------------------------------------------------
struct shared_cache * shared_cache_new(void);
void shared_cache_init(struct shared_cache * cache);
void shared_cache_destroy(struct shared_cache * cache);
void shared_cache_delete(struct shared_cache * cache);
//---------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
struct index {
  uint32_t id;
  uint32_t mvcc_id;           // MVCC id
  uint32_t key_size;
  uint32_t data_size;
  uint32_t block_index_size;
  union {
    struct {
      uint32_t leaf_entry_size;
      uint32_t node_entry_size;
    };
    uint32_t entries_sizes[2];
  };
  union {
    struct {
      uint32_t leaf_max_key_count;
      uint32_t node_max_key_count;
    };
    uint32_t blk_max_keys[2];
  };

  struct shared_cache * cache;

  struct rwlock readers_lock;

  array changed_links;
  uint8_t changed_links_static_data[sizeof(struct index_link_change) * 256];
  struct rwlock changed_links_lock;

  array idx_file_handles;
  uint8_t idx_file_handles_static_data[sizeof(int) * 8];
  array jrn_file_handles;
  struct mutex file_handles_mutex;

  array blocks_map;
  uint8_t blocks_map_static_data[sizeof(struct index_block_mapper_entry) * 256];
  struct rwlock blocks_map_lock;

  string data_dir;

  struct thread write_journal_thread;
  struct semaphore write_journal_wait;

  vector mvcc_indexes_pool;
  struct semaphore mvcc_wait;
  struct semaphore mvcc_lock;
  struct thread_pool mvcc_thread_pool;
  uint32_t mvcc_ref_count;
  uint32_t mvcc_pool_size;
  uint32_t mvcc_waiters;
  uintmax_t mvcc_count;
  bool mvcc_inprogress;
};
//---------------------------------------------------------------------------
void index_init_runtime(void);
void index_destroy_runtime(void);
struct index * index_new(void);
void index_init(struct index * idx);
void index_delete(struct index * idx);
void index_destroy(struct index * idx);
void index_set_key_size(struct index * idx,uintptr_t key_size);
void index_set_data_size(struct index * idx,uintptr_t data_size);
void index_set_block_index_size(struct index * idx,uintptr_t block_index_size);
struct index * index_get_mvcc(struct index * idx);
void index_put_mvcc(struct index * idx,struct index * mvcc_idx);
//---------------------------------------------------------------------------
struct index_btree_stack_entry {
  intptr_t c;
  uintptr_t position;
  intptr_t next_level_position;

  union {
    struct {
      struct index_link * link;
      struct index_link * left;
      struct index_link * right;
    };
    struct index_link * links[3];
  };
  union {
    struct {
      enum INDEX_LINK_CHANGE link_change;
      enum INDEX_LINK_CHANGE left_change;
      enum INDEX_LINK_CHANGE right_change;
    };
    enum INDEX_LINK_CHANGE links_change[3];
  };
  //union {
  //  struct {
  //    enum RWLOCK_TYPE link_lock;
  //    enum RWLOCK_TYPE left_lock;
  //    enum RWLOCK_TYPE right_lock;
  //  };
  //  enum RWLOCK_TYPE links_locks[3];
  //};
};
//---------------------------------------------------------------------------
void index_btree_stack_entry_init(struct index_btree_stack_entry * e);
//---------------------------------------------------------------------------
bool index_is_block_index_extent(struct index * idx,const byte_size_int * block_index);
void index_block_allocate(struct index * idx,byte_size_int * block_index);
void index_block_release(struct index * idx,const byte_size_int * block_index);
//---------------------------------------------------------------------------
struct index_btree_stack {
  intptr_t sp;
  array entries;
  uint8_t entries_static_data[sizeof(struct index_btree_stack_entry) * 8];
};
//---------------------------------------------------------------------------
struct index_btree_stack * index_btree_stack_new(void);
void index_btree_stack_init(struct index_btree_stack * s);
void index_btree_stack_destroy(struct index_btree_stack * s);
void index_btree_stack_delete(struct index_btree_stack * s);
//---------------------------------------------------------------------------
void index_get_file_filter(struct index * idx,enum INDEX_FILE_TYPE type,string * filter,uintptr_t mvcc_id);
void index_get_file_name(struct index * idx,enum INDEX_FILE_TYPE type,uintptr_t file_index,string * name);
int index_get_file_handle(struct index * idx,enum INDEX_FILE_TYPE type,uintptr_t file_index,bool create);
void index_put_file_handle(struct index * idx,enum INDEX_FILE_TYPE type,uintptr_t file_index,int file_handle);
void index_close_file_handles(struct index * idx,enum INDEX_FILE_TYPE type,uintptr_t file_index);
//---------------------------------------------------------------------------
struct index_link * index_link_block_v(struct index * idx,const byte_size_int * block_index,bool read);
//---------------------------------------------------------------------------
static __inline__ struct index_link * index_link_block(
  struct index * idx,const byte_size_int * block_index)
{
  return index_link_block_v(idx,block_index,true);
}
//---------------------------------------------------------------------------
static __inline__ struct index_link * index_link_block_noread(
  struct index * idx,const byte_size_int * block_index)
{
  return index_link_block_v(idx,block_index,false);
}
//---------------------------------------------------------------------------
void index_read_block(struct index * idx,struct index_link * link);
void index_write_block(struct index * idx,struct index_link * link);
//---------------------------------------------------------------------------
void index_shrink_cache(struct index * idx);
//---------------------------------------------------------------------------
void index_copy_paste_keys(
  struct index_link * dst_link,
  uint32_t dst_position,
  struct index_link * src_link,
  uint32_t src_position,
  uintptr_t count);
void index_cut_paste_keys(
  struct index_link * dst_link,
  uint32_t dst_position,
  struct index_link * src_link,
  uint32_t src_position,
  uintptr_t count);
void index_stack_entry_lookup_key(struct index_btree_stack_entry * e,const void * key);
void index_unlock_stack(struct index * idx,struct index_btree_stack * stack);
void index_unwind_stack(struct index * idx,struct index_btree_stack * stack,enum RWLOCK_TYPE lock);
//---------------------------------------------------------------------------
void index_get_key_path(
  struct index * idx,
  struct index_btree_stack * stack,
  const void * key,
  intptr_t direction);
//---------------------------------------------------------------------------
void index_scatter(
  struct index * idx,
  struct index_btree_stack * stack,
  const void * key,
  const void * data);
//---------------------------------------------------------------------------
void index_gather(
  struct index * idx,
  struct index_btree_stack * stack,
  const void * key,
  void * data);
//---------------------------------------------------------------------------
void index_lookup(
  struct index * idx,
  struct index_btree_stack * stack,
  const void * key,
  void * data,
  struct index ** p_mvcc_idx);
//---------------------------------------------------------------------------
void index_update_key(
  struct index * idx,
  struct index_btree_stack * stack,
  const void * key,
  const void * data);
//---------------------------------------------------------------------------
void index_shrink_cache(struct index * idx);
void index_write_journal(struct index * idx);
void index_write_journal_entry(struct thread * thr,struct index * idx);
void index_flush_journal(struct index * idx);
void index_drop(struct index * idx);
//---------------------------------------------------------------------------
#ifndef NDEBUG
void DEBUG_index_write_changed_blocks(struct index * idx);
void DEBUG_index_dump_block_keys(FILE * stream,struct index_link * link);
#else
#define DEBUG_index_write_changed_blocks(idx)
#define DEBUG_index_dump_block_keys(stream,link);
#endif
//---------------------------------------------------------------------------
static __inline__
uint8_t * index_block_entry(struct index_link * link,uintptr_t position)
{
  return link->blk.keys + link->idx->entries_sizes[link->blk.type] * position;
}
//---------------------------------------------------------------------------
#ifdef __cplusplus
}
#endif
//---------------------------------------------------------------------------
#endif // __idx_h__
//---------------------------------------------------------------------------
